import subprocess

import requests, time, os, re, json
from openpyxl import load_workbook
from requests_toolbelt import MultipartEncoder
import random, string
from io import BytesIO

class Setting:
    # 浏览器配置
    USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36'
    # 基础配置
    HOST_URL = 'https://192.168.0.1'
    ENTER_URL = '/cgi-bin/luci/admin'
    LOCKED_URL = '/cgi-bin/luci/admin/idu/checkLockedStatus'
    LOGIN_URL = '/cgi-bin/luci/admin/idu/login'
    HOME_URL = '/cgi-bin/luci/admin/5G-mifi'
    STATUS_URL = '/cgi-bin/luci/admin/idu/home/getConnectedStatusAdmin'
    # REBOOT_URL = '/cgi-bin/luci/admin/idu/device_reboot'
    UPGRADE_URL = '/cgi-bin/luci/admin/idu/sysuploadANDupgrade'
    Version = ''

setting = Setting()

def extract_value(pattern, text, logger, group_index=0):
    """使用正则表达式提取值"""
    logger.debug(f"Extracting value with pattern: {pattern}")
    match = re.search(pattern, text)
    if match:
        return match.group(1)
    logger.warning(f"Pattern not found: {pattern}")
    return None

def get_sys_unique_id(response_text, logger):
    """获取sysUniqueid"""
    pattern = r"\"sysUniqueid\",'(.+?)'"
    return extract_value(pattern, response_text, logger)

def get_token_val(response_text, logger):
    """获取token值"""
    pattern = r"tokenVal = '(.+?)'"
    return extract_value(pattern, response_text, logger)

def get_current_version(response_text, logger):
    """获取currentVersion"""
    pattern = r'"firmwareVersion\": \"(.+?)\"'
    return extract_value(pattern, response_text, logger)

def send_request(session, method, url, logger, **kwargs):
    """发送HTTP请求（带重试机制）"""
    logger.info(f"Sending {method} request to {url}")
    # response = session.get(url='https://192.168.0.1/cgi-bin/luci/admin', **kwargs)
    # return response
    try:
        if method == 'GET':
            response = session.get(url, **kwargs)
        elif method == 'POST':
            response = session.post(url, **kwargs)
        if response.status_code == 403 and url == 'https://192.168.0.1/cgi-bin/luci/admin':
            logger.debug(f"Resonse status: {response.status_code}")
            logger.debug("The expect status_code of 'https://192.168.0.1/cgi-bin/luci/admin' is 403")
            return response
        response.raise_for_status()
        logger.debug(f"Response status: {response.status_code}")
        return response
    except requests.exceptions.RequestException as e:
        logger.error(f"Request failed: {str(e)}")
        raise

def list_files_in_directory(path):
    # 定位版本路径是否存在，以列表形式返回路径下的文件
    try:
        files = os.listdir(path)
        return files
    except FileNotFoundError:
        return "指定路径不存在"
    except NotADirectoryError:
        return "指定路径不是一个目录"

def get_boundary_data(fields, logger):
    """生成multipart/form-data边界数据"""
    logger.debug("Generating boundary data")
    boundary = '----WebKitFormBoundary' + ''.join(random.sample(string.ascii_letters + string.digits, 16))
    m_data = MultipartEncoder(fields, boundary=boundary)
    return {
        "content_type": m_data.content_type,
        "data": m_data.to_string()
    }

def create_login_data(token, username, password, logger):
    """创建登录数据"""
    logger.debug("Creating login data")
    return {
        "token": token,
        "luci_username": username,
        "luci_pwd": password
    }

def create_get_status_data(token, logger):
    """创建获取状态数据"""
    logger.debug("Creating get status data")
    return {
        "token": token
    }

def create_file_buffer(file_path):
    with open(file_path, 'rb') as f:
        return BytesIO(f.read())

def get_login_page(session, logger):
    """获取登录页面"""
    logger.info("Getting login page")
    url = setting.HOST_URL + setting.ENTER_URL
    response = send_request(session, 'GET', url, logger)
    # print(response.status_code)
    return response.text

def check_loginLocked(session, logger):
    """检查是否上锁，解除登录限制"""
    logger.info("Checking loginLocked")
    url = setting.HOST_URL + setting.LOCKED_URL
    response = send_request(session, 'POST', url, logger)
    return response.text

def perform_login(session, logger, login_fields):
    """执行登录操作"""
    logger.info("Performing login")
    url = setting.HOST_URL + setting.LOGIN_URL
    
    # 生成边界数据
    boundary_data = get_boundary_data(login_fields, logger)
    
    # 发送登录请求
    headers = {'Content-Type': boundary_data['content_type']}
    response = send_request(
        session, 'POST', url, logger,
        headers=headers, 
        data=boundary_data['data']
    )
    return response.text

def get_home_page(session, logger):
    """获取首页"""
    logger.info("Getting home page")
    url = setting.HOST_URL + setting.HOME_URL
    response = send_request(session, 'GET', url, logger)
    return response.text

def get_connected_status_admin_page(session, get_status_fields, logger):
    """获取当前设备状态信息"""
    logger.info("Getting Connected Status Admin page")
    url = setting.HOST_URL + setting.STATUS_URL

    # 生成边界数据
    boundary_data = get_boundary_data(get_status_fields, logger)
    
    # 发送登录请求
    headers = {'Content-Type': boundary_data['content_type']}
    response = send_request(
        session, 'POST', url, logger,
        headers=headers, 
        data=boundary_data['data']
    )
    return response.text

# def get_ver(auth_dic, logger):
#     requests.packages.urllib3.disable_warnings()
#     session = requests.Session()
#     session.headers.update(
#         {'User-Agent': setting.USER_AGENT}
#     )
#     session.cookies = auth_dic['cookie']
#     session.verify = False


#     logger.info("get connected status")

def get_ver(web_user, web_pwd, logger):
    """完整认证流程"""
    requests.packages.urllib3.disable_warnings()
    session = requests.Session()
    session.headers.update(
        {'User-Agent': setting.USER_AGENT}
    )
    session.verify = False


    logger.info("Starting authentication process")
    
    # 步骤1: 获取登录页面
    login_page = get_login_page(session, logger)
    sys_unique_id = get_sys_unique_id(login_page, logger)
    # print('sys_unique_id: ', sys_unique_id)

    # 步骤2：解除登录限制
    unlocked_page = check_loginLocked(session, logger)
    # print(unlocked_page)

    # 步骤3: 登录请求
    first_login_data = create_login_data(sys_unique_id, web_user, web_pwd, logger)
    perform_login(session, logger, first_login_data)

    
    # 步骤3: 第二次登录请求（带密码）(Gamma6取消2次登录流程)
    # second_login_data = AuthFactory.create_login_data(sys_unique_id)
    # self.perform_login(second_login_data)
    
    # 步骤4: 获取首页并提取token
    home_page = get_home_page(session, logger)
    token = get_token_val(home_page, logger)
    # 获取cookie
    cookies = session.cookies.get_dict()
    cookie_str = "; ".join([f"{k}={v}" for k, v in cookies.items()])
    logger.info("Authentication successful")
    # return 
    ready_dic = {'token': token, 'cookie': cookie_str}
    # print('ready_dic: ', ready_dic)

    get_status_data = create_get_status_data(ready_dic['token'], logger)
    get_status_page = get_connected_status_admin_page(session, get_status_data, logger)
    ver1 = get_current_version(get_status_page, logger)
    setting.Version = ver1
    return ver1



def upgrade(web_user, web_pwd, fw_path, logger):
    """完整认证流程"""
    requests.packages.urllib3.disable_warnings()
    session = requests.Session()
    session.headers.update(
        {'User-Agent': setting.USER_AGENT}
    )
    session.verify = False


    logger.info("Starting authentication process")
    
    # 步骤1: 获取登录页面
    login_page = get_login_page(session, logger)
    sys_unique_id = get_sys_unique_id(login_page, logger)
    # print('sys_unique_id: ', sys_unique_id)

    # 步骤2：解除登录限制
    unlocked_page = check_loginLocked(session, logger)
    # print(unlocked_page)

    # 步骤3: 登录请求
    first_login_data = create_login_data(sys_unique_id, web_user, web_pwd, logger)
    perform_login(session, logger, first_login_data)

    
    # 步骤3: 第二次登录请求（带密码）(Gamma6取消2次登录流程)
    # second_login_data = AuthFactory.create_login_data(sys_unique_id)
    # self.perform_login(second_login_data)
    
    # 步骤4: 获取首页并提取token
    home_page = get_home_page(session, logger)
    token = get_token_val(home_page, logger)
    # 获取cookie
    cookies = session.cookies.get_dict()
    cookie_str = "; ".join([f"{k}={v}" for k, v in cookies.items()])
    logger.info("Authentication successful")
    # return 
    ready_dic = {'token': token, 'cookie': cookie_str}
    # print('ready_dic: ', ready_dic)

    # get_status_data = create_get_status_data(ready_dic['token'], logger)
    # get_status_page = get_connected_status_admin_page(session, get_status_data, logger)
    # ver1 = get_current_version(get_status_page, logger)
    # setting.Version = ver1
    # print("setting.Version: ", setting.Version)



    """上传固件并升级"""
    # files = list_files_in_directory(fw_path)
    packet_size = os.path.getsize(fw_path)
    file_buf = create_file_buffer(fw_path)

    uploadPacket = {
        "token": ready_dic['token'],
        "operate": 'upload',
        "image": None,
        "fileName": os.path.split(fw_path)[-1],
        "step": '1',
        "fileSize": str(packet_size)
    }
    file_buf.seek(0)
    packet = (os.path.split(fw_path)[-1], file_buf, 'application/x-gzip')
    uploadPacket['image'] = packet


    # 上传使服务端(DUT)验证版本是否正确
    m_data_3 = get_boundary_data(uploadPacket, logger)
    r_4_headers = {'Content-Type': m_data_3['content_type']}
    url = setting.HOST_URL + setting.UPGRADE_URL
    # r_4 = session.post(url=setting.HOST_URL+setting.UPGRADE_URL, headers=r_4_headers, data=m_data_3.to_string(), verify=False)
    logger.info("Upload Firmware packet - step 1/2")
    r_4 = send_request(
        session, 'POST', url, logger,
        headers=r_4_headers,
        data=m_data_3['data']
    )
    
    # print(r_4)



    file_buf.seek(0)
    packet = (os.path.split(fw_path)[-1], file_buf, 'application/x-gzip')
    # 版本上传表单2(直接更新参数)
    uploadPacket['step'] = '2'
    uploadPacket['image'] = packet

    # 二次上传验证，在服务端(DUT)建立路径，为upgrade作准备
    m_data_4 = get_boundary_data(uploadPacket, logger)
    r_5_headers = {'Content-Type': m_data_4['content_type']}
    # r_5 = s.post(url=host_url+upgrade_url, headers=r_5_headers, data=m_data_4.to_string(), verify=False)
    logger.info("Upload Firmware packet - step 2/2")
    r_5 = send_request(
        session, 'POST', url, logger,
        headers=r_5_headers,
        data=m_data_4['data']
    )
    # print(r_5)

    file_buf.seek(0)
    packet = (os.path.split(fw_path)[-1], file_buf, 'application/x-gzip')
    uploadPacket['image'] = packet
    # 版本升级表单，执行升级操作
    upgradePacket = {
        "token": ready_dic['token'][0],
        "operate": 'upgrade',
        "image": packet,
        "fileName": os.path.split(fw_path)[-1]
    }

    m_data_5 = get_boundary_data(upgradePacket, logger)
    r_6_headers = {'Content-Type': m_data_5['content_type']}
    # r_6 = s.post(url=host_url+upgrade_url, headers=r_6_headers, data=m_data_5.to_string(), verify=False)
    logger.info("Firmware Upgrade")
    try:
        r_6 = send_request(
            session, 'POST', url, logger,
            headers=r_6_headers,
            data=m_data_5['data']
        )
    except Exception as e:
        logger.info(f"Firmware upgrade success: {str(e)}")
    finally:
        session.close()

    file_buf.close()

    return r_5.text


def upgrade_dut(old_fw, new_fw, host, web_user, web_pwd, loop, logger, func):
    # rsa_pwd = pass_k(host, web_pwd, logger)
    fw_upgrade(old_fw, new_fw, host, web_user, web_pwd, loop, logger, func)

def fw_upgrade(older_fw, newer_fw, host, user, pwd, loop, logger, func):
    # print('older_fw: ', older_fw)
    # print('newer_fw: ', newer_fw)

    result = {}
    # 创建报告目录
    report_path = os.getcwd() + '\\report\\' + time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
    os.makedirs(report_path)
    # 加载模板文件
    wb = load_workbook(filename='template_fw.xlsx')
    ws = wb['fw']
    # 重启设备
    # reboot(host,user,pwd)
    # 循环执行固件升级测试
    for i in range(1, loop + 1):
        logger.info(f"第{i}次测试")
        func(f"第{i}次测试")
        result[i] = {}
        # 遍历要升级的固件文件
        for file in (older_fw, newer_fw):
            logger.info('开始升级，目标固件{0}'.format(file))
            func('开始升级，目标固件{0}'.format(file))
            # 获取固件版本号
            test_ver = re.findall("J21W005_(.+)_", file)[0]
            # print('test_ver: ', test_ver, type(test_ver))
            # print('test_ver: ', test_ver, type(test_ver), test_ver[0], type(test_ver[0]))
            # 获取固件文件路径
            # fw_path = file
            # fw_path_split = os.path.split(file)
            # print('fw_path: ', fw_path)
            # print('fw_path_split: ', fw_path_split)
            # 获取设备当前版本号
            # ver = get_ver(host, user, logger)
            # 升级固件
            func("升级中，请耐心等待")

            rpc_log = upgrade(user, pwd, file, logger)
            print('rpc_log: ', rpc_log)

            # 等待设备恢复联网,每10s ping一次
            func('升级完成，设备重启中，等待恢复网络')
            time.sleep(160)
            while True:
                response = subprocess.run(['ping', '-n', '1', host], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                if b'TTL' in response.stdout:
                    logger.info(f"ping [{host}] 成功，网络恢复")
                    func(f"ping [{host}] 成功，网络恢复")
                    break
                else:
                    logger.info(f"ping [{host}] 失败,10s后继续检测网络")
                    func(f"ping [{host}] 失败,10s后继续检测网络")
                    time.sleep(10)
            func('设备重启成功，网络已恢复，验证升级结果')
            # 获取升级后的设备版本号
            ver1 = get_ver(user, pwd, logger)
            # ver1 = setting.Version
            # 判断升级是否成功
            if ver1 == test_ver:
                result[i][test_ver] = 'Passed'
            else:
                result[i][test_ver] = 'Failed'
            
            # 保存升级日志
            result[i][test_ver + '_Log'] = rpc_log
            logger.info(f'测试固件版本:{test_ver}, 升级后系统版本:{ver1}, 测试结果:{result[i][test_ver]}')
            func(f'测试固件版本:{test_ver}, 升级后系统版本:{ver1}, 测试结果:{result[i][test_ver]}')
        # 判断本次循环的升级结果
        print('list(result[i].values()): ', list(result[i].values()), 'len(list(result[i].values()): ', len(list(result[i].values())))
        if 'Failed' not in list(result[i].values()) and len(list(result[i].values())) == 4:
            result[i]['Result'] = 'Passed'
        else:
            result[i]['Result'] = 'Failed'
        logger.info(f"第{i}次测试结束")
        func(f"第{i}次测试结束，测试结果:{result[i]['Result']}")
        # ver1 = ''
        # 保存测试结果到Excel文件
        judge = {}
        test_ver1 = re.findall("J21W005_(.+)_", older_fw)[0]
        test_ver2 = re.findall("J21W005_(.+)_", newer_fw)[0]
        logger.info(result)
        for get in result.items():
            temp = get[1]
            ws.cell(column=3, row=1).value = test_ver1
            ws.cell(column=4, row=1).value = test_ver1 + "_Log"
            ws.cell(column=5, row=1).value = test_ver2
            ws.cell(column=6, row=1).value = test_ver2 + "_Log"
            ws.cell(column=1, row=1 + get[0]).value = str(get[0])
            ws.cell(column=2, row=1 + get[0]).value = temp['Result']
            ws.cell(column=3, row=1 + get[0]).value = temp[test_ver1]
            ws.cell(column=4, row=1 + get[0]).value = temp[test_ver1 + '_Log']
            ws.cell(column=5, row=1 + get[0]).value = temp[test_ver2]
            ws.cell(column=6, row=1 + get[0]).value = temp[test_ver2 + '_Log']
            judge[str(get[0])] = temp['Result']
        wb.save('{}\\{}.xlsx'.format(report_path, 'fw_upgrade'))
    # # 判断整个测试过程的结果
    logger.info('upgarde result:{0}'.format(list(judge.values())))
    func ('测试结果:{0}'.format(list(judge.values())))
    # assert 'Failed' not in list(judge.values()) and len(list(judge.values())) == loop, 'upgrade test failed'

